#!/usr/bin/env python

# Copyright 2016 WebAssembly Community Group participants
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from support import run_command
from shared import (
    fail, fail_with_error, fail_if_not_contained,
    options, S2WASM, WASM_SHELL
)


def test_s2wasm():
  print '\n[ checking .s testcases... ]\n'

  cmd = S2WASM + [
      os.path.join(options.binaryen_test, 'dot_s', 'basics.s'),
      '--import-memory']
  output = run_command(cmd)
  fail_if_not_contained(
      output, '(import "env" "memory" (memory $0 1))')

  extension_arg_map = {
      '.wast': [],
      '.clamp.wast': ['--trap-mode=clamp'],
      '.js.wast': ['--trap-mode=js'],
      '.jscall.wast': ['--emscripten-reserved-function-pointers=3'],
  }
  for dot_s_dir in ['dot_s', 'llvm_autogenerated']:
    dot_s_path = os.path.join(options.binaryen_test, dot_s_dir)
    for s in sorted(os.listdir(dot_s_path)):
      if not s.endswith('.s'):
        continue
      print '..', s
      for ext, ext_args in extension_arg_map.iteritems():
        wasm = s.replace('.s', ext)
        expected_file = os.path.join(options.binaryen_test, dot_s_dir, wasm)
        expected_exists = os.path.exists(expected_file)
        if ext != '.wast' and not expected_exists:
          continue

        full = os.path.join(options.binaryen_test, dot_s_dir, s)
        stack_alloc = (['--allocate-stack=1024']
                       if dot_s_dir == 'llvm_autogenerated'
                       else [])
        cmd = S2WASM + [full, '--emscripten-glue'] + stack_alloc + ext_args
        if s.startswith('start_'):
          cmd.append('--start')
        actual = run_command(cmd)

        # verify output
        if not expected_exists:
          print actual
          fail_with_error('output ' + expected_file + ' does not exist')
        expected = open(expected_file, 'rb').read()
        if actual != expected:
          fail(actual, expected)

        # verify with options
        cmd = S2WASM + [full, '--global-base=1024'] + stack_alloc
        run_command(cmd)

        # run wasm-shell on the .wast to verify that it parses
        cmd = WASM_SHELL + [expected_file]
        run_command(cmd)


def test_linker():
  print '\n[ running linker tests... ]\n'
  # The {main,foo,bar,baz}.s files were created by running clang over the
  # respective c files. The foobar.bar archive was created by running:
  # llvm-ar -format=gnu rc foobar.a quux.s foo.s bar.s baz.s
  cmd = S2WASM + [
      os.path.join(options.binaryen_test, 'linker', 'main.s'), '-l',
      os.path.join(options.binaryen_test, 'linker', 'archive', 'foobar.a')]
  output = run_command(cmd)
  # foo should come from main.s and return 42
  fail_if_not_contained(output, '(func $foo')
  fail_if_not_contained(output, '(i32.const 42)')
  # bar should be linked in from bar.s
  fail_if_not_contained(output, '(func $bar')
  # quux should be linked in from bar.s even though it comes before bar.s in
  # the archive
  fail_if_not_contained(output, '(func $quux')
  # baz should not be linked in at all
  if 'baz' in output:
    fail_with_error('output should not contain "baz": ' + output)

  # Test an archive using a string table
  cmd = S2WASM + [
      os.path.join(options.binaryen_test, 'linker', 'main.s'), '-l',
      os.path.join(options.binaryen_test, 'linker', 'archive', 'barlong.a')]
  output = run_command(cmd)
  # bar should be linked from the archive
  fail_if_not_contained(output, '(func $bar')

  # Test exporting memory growth function and emscripten runtime functions
  cmd = S2WASM + [
      os.path.join(options.binaryen_test, 'linker', 'main.s'),
      '--emscripten-glue', '--allow-memory-growth']
  output = run_command(cmd)
  expected_funcs = [
      ('__growWasmMemory', '(param $newSize i32)'),
      ('stackSave', '(result i32)'),
      ('stackAlloc', '(param $0 i32) (result i32)'),
      ('stackRestore', '(param $0 i32)'),
  ]
  for name, extra in expected_funcs:
    space = ' ' if extra else ''
    fail_if_not_contained(output, '(export "{0}" (func ${0}))'.format(name))
    for line in output.split('\n'):
      if '(func ${0}'.format(name + space) in line:
        # we found the relevant line for the function definition. remove
        # a (; X ;) comment with its index
        start = line.find('(; ')
        if start >= 0:
          end = line.find(' ;)')
          line = line[:start] + line[end + 4:]
        fail_if_not_contained(line, '(func ${0}'.format(name + space + extra))


if __name__ == '__main__':
  test_s2wasm()
  test_linker()
